// Copyright (c) 2006, Gregory Todd <greg@oddsim.com>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// 
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace Othello.Engine
{
    public class Player
    {
        public static TreeNode treeNode;
        public int playerColor;

        public bool computer;

        public int difficulty;

        /// <summary>
        /// Create a human player
        /// </summary>
        public Player()
        {
            this.computer = false;
        }

        /// <summary>
        /// Create a computer player
        /// </summary>
        /// <param name="difficulty">Difficulty of the computer player</param>
        public Player(int difficulty)
        {
            this.computer = true;
            this.difficulty = difficulty;
        }

        public void AlphaBeta(Board board)
        {
            treeNode = new TreeNode();
            AlphaBeta(board, difficulty, playerColor, int.MinValue, int.MaxValue, treeNode);
        }

        public int AlphaBeta(Board board, int depth, int currentPlayer, int alpha, int beta, TreeNode node)
        {
            TreeNode thisNode = new TreeNode();
            node.Nodes.Add(thisNode);

            // If the game is over, terminate the search
            if (board.GameOver())
            {
                int value = board.StaticEvaluator(this.playerColor);
                thisNode.Text = value.ToString();
                return value;
            }

            // At the bottom of the search space
            if (depth == 0)
            {
                int value = board.StaticEvaluator(this.playerColor);
                thisNode.Text = value.ToString();
                return value;
            }

            // If this player can't play, skip turn
            if (!board.CanPlay(currentPlayer))
            {
                int value = AlphaBeta(board, depth, -currentPlayer, alpha, beta, thisNode);
                thisNode.Text = "Skip";
                return value;
            }

            // Get the list of plausable moves, sorted by most pieces flipped
            List<Move> moves = board.MoveGenerator(currentPlayer);
            moves.Sort();


            // Find the best currentMove
            Move bestMove = Move.Empty;
            foreach (Move currentMove in moves)
            {
                // Copy the board
                Board currentBoard = board.Copy();
                // Make the selected move
                currentBoard.Move(currentMove.x, currentMove.y, currentPlayer);
                // Evaluate the board
                int currentValue = AlphaBeta(currentBoard, depth - 1, -currentPlayer, alpha, beta, thisNode);

                // MAX
                if (currentPlayer == this.playerColor)
                {
                    if (currentValue > alpha)
                    {
                        alpha = currentValue;
                        bestMove = currentMove;
                    }
                    // Prune?
                    if (alpha >= beta)
                    {
                        thisNode.Text += String.Format("MAX >= {0} ({1}/{2})", alpha, moves.IndexOf(currentMove) + 1, moves.Count);
                        return alpha;
                    }
                }
                // MIN
                else
                {
                    if (currentValue < beta)
                    {
                        beta = currentValue;
                        bestMove = currentMove;
                    }
                    // Prune?
                    if (alpha >= beta)
                    {
                        thisNode.Text += String.Format("MIN <= {0} ({1}/{2})", beta, moves.IndexOf(currentMove) + 1, moves.Count);
                        return beta;
                    }
                }
            }

            // Make the best move
            if (!bestMove.Equals(Move.Empty))
                board.Move(bestMove.x, bestMove.y, currentPlayer);

            // Return the value of the best move
            if (currentPlayer == this.playerColor)
            {
                thisNode.Text += String.Format("MAX = {0} ({1}/{2}) [{3}, {4}]", alpha, moves.Count, moves.Count, bestMove.x + 1, bestMove.y + 1);
                return alpha;
            }
            else
            {
                thisNode.Text += String.Format("MIN = {0} ({1}/{2}) [{3}, {4}]", beta, moves.Count, moves.Count, bestMove.x + 1, bestMove.y + 1);
                return beta;
            }
        }
        public int MiniMax(Board board, int depth, int currentPlayer, TreeNode parentNode)
        {
            // Create a related TreeNode
            TreeNode thisNode = new TreeNode();
            parentNode.Nodes.Add(thisNode);
            if (currentPlayer == this.playerColor)
                thisNode.Text = "MAX: ";
            else
                thisNode.Text = "MIN: ";


            // If the game is over, terminate the search
            if (board.GameOver())
            {
                int value = board.StaticEvaluator(this.playerColor);
                thisNode.Text += value.ToString();
                return value;
            }

            // At the bottom of the search space
            if (depth == 0)
            {
                int value = board.StaticEvaluator(this.playerColor);
                thisNode.Text += value.ToString();
                return value;
            }

            // If this player can't play, skip turn
            if (!board.CanPlay(currentPlayer))
            {
                int value = MiniMax(board, depth, -currentPlayer, thisNode);
                thisNode.Text += value.ToString();
                return value;
            }

            // Get the list of plausable moves, sorted by most pieces flipped
            List<Move> moves = board.MoveGenerator(currentPlayer);

            // Find the best move avaiable
            Move bestMove = Move.Empty;
            int bestValue;
            if (currentPlayer == this.playerColor)
                bestValue = Int32.MinValue;
            else
                bestValue = Int32.MaxValue;

            foreach (Move currentMove in moves)
            {
                // Copy the board
                Board currentBoard = board.Copy();
                // Make the selected move
                currentBoard.Move(currentMove.x, currentMove.y, currentPlayer);
                // Evaluate the board
                int currentValue = MiniMax(currentBoard, depth - 1, -currentPlayer, thisNode);

                // MAX
                if (currentPlayer == this.playerColor)
                {
                    if (currentValue > bestValue)
                    {
                        bestValue = currentValue;
                        bestMove = currentMove;
                    }
                }
                // MIN
                else
                {
                    if (currentValue < bestValue)
                    {
                        bestValue = currentValue;
                        bestMove = currentMove;
                    }
                }
            }

            // Make the best move
            if (!bestMove.Equals(Move.Empty))
                board.Move(bestMove.x, bestMove.y, currentPlayer);

            thisNode.Text += bestValue.ToString();
            return bestValue;
        }
    }
}
